function varargout = jitter3(varargin)
% jitter3 Application M-file for jitter3.fig
%    FIG = jitter3 launch jitter3 GUI.
%    jitter3('callback_name', ...) invoke the named callback.

% Last Modified by GUIDE v2.0 24-Apr-2001 16:59:06

if nargin == 0  % LAUNCH GUI
    
    fig = openfig(mfilename,'reuse');
    
    % Use system color scheme for figure:
    set(fig,'Color',get(0,'defaultUicontrolBackgroundColor'));
    
    % Generate a structure of handles to pass to callbacks, and store it. 
    handles = guihandles(fig);
    guidata(fig, handles);
    
    if nargout > 0
        varargout{1} = fig;
    end
    
elseif ischar(varargin{1}) % INVOKE NAMED SUBFUNCTION OR CALLBACK
    
    try
        [varargout{1:nargout}] = feval(varargin{:}); % FEVAL switchyard
    catch
        disp(lasterr);
    end
    
end


%| ABOUT CALLBACKS:
%| GUIDE automatically appends subfunction prototypes to this file, and 
%| sets objects' callback properties to call them through the FEVAL 
%| switchyard above. This comment describes that mechanism.
%|
%| Each callback subfunction declaration has the following form:
%| <SUBFUNCTION_NAME>(H, EVENTDATA, HANDLES, VARARGIN)
%|
%| The subfunction name is composed using the object's Tag and the 
%| callback type separated by '_', e.g. 'slider2_Callback',
%| 'figJitter3_CloseRequestFcn', 'axis1_ButtondownFcn'.
%|
%| H is the callback object's handle (obtained using GCBO).
%|
%| EVENTDATA is empty, but reserved for future use.
%|
%| HANDLES is a structure containing handles of components in GUI using
%| tags as fieldnames, e.g. handles.figJitter3, handles.slider2. This
%| structure is created at GUI startup using GUIHANDLES and stored in
%| the figure's application data using GUIDATA. A copy of the structure
%| is passed to each callback.  You can store additional information in
%| this structure at GUI startup, and you can change the structure
%| during callbacks.  Call guidata(h, handles) after changing your
%| copy to replace the stored original so that subsequent callbacks see
%| the updates. Type "help guihandles" and "help guidata" for more
%| information.
%|
%| VARARGIN contains any extra arguments you have passed to the
%| callback. Specify the extra arguments by editing the callback
%| property in the inspector. By default, GUIDE sets the property to:
%| <MFILENAME>('<SUBFUNCTION_NAME>', gcbo, [], guidata(gcbo))
%| Add any extra arguments after the last argument, before the final
%| closing parenthesis.


% --------------------------------------------------------------------
function varargout = pushbuttonSINGLE_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.pushbuttonSINGLE.
% disable the button while processing the acquisition
set(h,'Enable','off');
% store application data with the main figure object
% it is updated by edit box callbacks
g = getappdata(handles.figJitter3,'instr');
recordLen = getappdata(handles.figJitter3,'RecordLength');
symbolRate = getappdata(handles.figJitter3,'SymbolRate');
threshold = getappdata(handles.figJitter3,'Threshold');
hysteresis = getappdata(handles.figJitter3,'Hysteresis');
exportWaveform = getappdata(handles.figJitter3,'ExportWaveform');
% call the function that communicates with the instrument
acquire_instrument(handles,g,symbolRate,recordLen,...
    threshold,hysteresis,exportWaveform)
% enable the button
set(h,'Enable','on');


% --------------------------------------------------------------------
function varargout = pushbuttonClose_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.pushbuttonClose.
% get the instrument object and delete it unless it is empty 
% (CONNECT button was never pressed)
g = getappdata(handles.figJitter3,'instr');
if isempty(g)
    disp('No instrument object')
else
    fclose(g)
    delete(g)
    disp('Instrument object is closed and deleted')
end
close(handles.figJitter3)

% --------------------------------------------------------------------
function varargout = editSymbolRate_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.editSymbolRate.
setappdata(handles.figJitter3,'SymbolRate',str2num(get(h,'String')));

% --------------------------------------------------------------------
function varargout = editRecordLength_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.editRecordLength.
setappdata(handles.figJitter3,'RecordLength',str2num(get(h,'String')));


% --------------------------------------------------------------------
function varargout = editThreshold_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.editThreshold.
setappdata(handles.figJitter3,'Threshold',str2num(get(h,'String')));


% --------------------------------------------------------------------
function varargout = editHysteresis_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.editHysteresis.
setappdata(handles.figJitter3,'Hysteresis',str2num(get(h,'String')));

% --------------------------------------------------------------------
function varargout = popupmenuSelector_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.popupmenuSelector.
setappdata(handles.figJitter3,'Connection',get(h,'Value'));

% --------------------------------------------------------------------
function varargout = pushbuttonCONNECT_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.pushbuttonCONNECT.

% Read the parameters from edit boxes (String property) on the user 
% interface and set the application data associated with the figure 
% window so that it is accessible from any callback function
symbolRate = str2num(get(handles.editSymbolRate,'String'));
setappdata(handles.figJitter3,'SymbolRate',symbolRate);
recordLen = str2num(get(handles.editRecordLength,'String'));
setappdata(handles.figJitter3,'RecordLength',recordLen);
threshold = str2num(get(handles.editThreshold,'String'));
setappdata(handles.figJitter3,'Threshold',threshold);
hysteresis = str2num(get(handles.editHysteresis,'String'));
setappdata(handles.figJitter3,'Hysteresis',hysteresis);
conn = get(handles.popupmenuSelector,'Value');
setappdata(handles.figJitter3,'Connection',conn);
strCh = 'ch1';
g = open_instrument(conn,strCh,symbolRate,recordLen,...
    threshold,hysteresis)
% store the instrument object as application data so that other 
% callbacks can access it
setappdata(handles.figJitter3,'instr',g)
% Turn the CONNECT button enable property off so that it can't be pressed again
set(h,'Enable','off');
set(handles.editRecordLength,'Enable','off');
set(handles.popupmenuSelector,'Enable','off');

% --------------------------------------------------------------------
function varargout = pushbuttonExportInstrument_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.pushbuttonExportInstrument.
g = getappdata(handles.figJitter3,'instr');
assignin('base','instr',g);
disp('Instrument object exported to workspace as instr')

% --------------------------------------------------------------------
function varargout = checkboxWaveformExport_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.checkboxWaveformExport.
setappdata(handles.figJitter3,'ExportWaveform',get(h,'Value'));



% --------------------------------------------------------------------
function varargout = pushbuttonStart_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.pushbuttonStart.

% set the application data interrupted to 0
% it will be changed only by pressing STOP button.
% this variable will be checked inside the while loop in acquire instrument
interrupted = 0;
setappdata(handles.figJitter3,'interrupted',interrupted);
set(h,'Enable','off');

g = getappdata(handles.figJitter3,'instr');
recordLen = getappdata(handles.figJitter3,'RecordLength');
symbolRate = getappdata(handles.figJitter3,'SymbolRate');
threshold = getappdata(handles.figJitter3,'Threshold');
hysteresis = getappdata(handles.figJitter3,'Hysteresis');
exportWaveform = getappdata(handles.figJitter3,'ExportWaveform');

% Change the scope to perform continuous measurements
fprintf(g,'ACQUIRE:STOPAFTER RUNSTOP');

% call the acquisition function
acquire_instrument(handles,g,symbolRate,recordLen,...
    threshold,hysteresis,exportWaveform)

% Enable the button when finished (STOP button pressed and 
% acquire_instrument finished)

set(h,'Enable','on');

% --------------------------------------------------------------------
function varargout = pushbuttonStop_Callback(h, eventdata, handles, varargin)
% Stub for Callback of the uicontrol handles.pushbuttonStop.
setappdata(handles.figJitter3,'interrupted', 1);

% function to open the instrument and set up the measurement
function g = open_instrument(conn,strCh,symbolRate,...
    recordLen,threshold,hysteresis)

% Use inside the scope with Tek VISA (conn=1),
% externally with NI visa (conn=2)
% or Agilent VISA (conn=3)

switch conn 
case 1,
    g = visa('tek','GPIB8::1::INSTR');
    disp('g = visa(''tek'',''GPIB8::1::INSTR'');')
case 2,
    g = visa('ni','GPIB0::1::INSTR');
    disp('g = visa(''ni'',''GPIB0::1::INSTR'');')
case 3,
    g = visa('agilent','GPIB0::1::INSTR');
    disp('g = visa(''agilent'',''GPIB0::1::INSTR'');')
end    
disp('Instrument object is created')

% set the instrument object properties
set(g,'InputBufferSize',recordLen*2);
% open the instrument object for reading and writing
fopen(g);
% send commands to set up the instrument
fprintf(g,'HEADER OFF');
fprintf(g,['DATA:SOURCE ' strCh]);
fprintf(g,'DATA:ENCDG SRIBINARY;WIDTH 2');
fprintf(g,'ACQUIRE:STATE OFF');
fprintf(g,'ACQUIRE:MODE NORMALSAMPLE');
fprintf(g,'ACQUIRE:STOPAFTER SEQUENCE');
% end of open_instrument


% ------------------------------------
function acquire_instrument(handles,g,symbolRate,recordLen,...
    threshold,hysteresis,exportWaveform)
% function to perform a measurement and read the waveform data
fprintf(g,'ACQUIRE:STATE RUN');

% set this variable to 0 despite what value is stored as an 
% application data. This enables both SINGLE and STOP/RUN functionality
interrupted = 0;

% perform the main loop
while (~interrupted)
    while query(g,'BUSY?','%s','%e'); end;
    horizLen = query(g,'HORIZONTAL:RECORD?','%s','%e');
    
    fprintf(g,['DATA:START ' num2str(1)]);
    fprintf(g,['DATA:STOP ' num2str(recordLen)]);
    fprintf(g,'CURVE?');
    dummy_string1 = fscanf(g,'%s',2);
    dummy_string2 = fscanf(g,'%s',str2num(dummy_string1(2)));
    recordLen2Transfer = min(recordLen,horizLen);
    [waveform_raw count] = fread(g,recordLen2Transfer,'int16');
    % read the termination character
    dummy_string3 = fscanf(g,'%s',1);
        
    % get the sampling interval 
    sampleInterval = query(g,'WFMOUTPRE:XINCR?','%s','%e');
    
    % Scale the data 
    yunit = query(g,'WFMOUTPRE:YUNIT?');
    ymult = query(g,'WFMOUTPRE:YMULT?','%s','%e');
    yoff = query(g,'WFMOUTPRE:YOFF?','%s','%e');
    yzero = query(g,'WFMOUTPRE:YZERO?','%s','%e');
    
    % check that all parameters were read from the device
    if ~(isempty(waveform_raw) | isempty(ymult) | isempty(yoff) |...
            isempty(yzero))
        % scale the data to the correct values
        waveform = ymult*(waveform_raw - yoff) - yzero;
        
        % determine whether waveform contains any edges
        % otherwise skip the jitter analysis
        if max(waveform) > threshold + hysteresis & ...
                min(waveform) < threshold - hysteresis
            % find the edges in the supplied waveform
            measuredTime = measureEdgeTiming2(waveform,threshold,hysteresis,sampleInterval);
            
            % preallocate space for the clocks array
            clocks=zeros(1,length(measuredTime));
            
            % derive the clocks based on the supplied symbol rate
            for index = 2:length(measuredTime);
                clocks(index) = (round(symbolRate * (measuredTime(index) - ...
                    measuredTime(index - 1)))) + clocks(index-1);
            end
            
            
            % fit the derived clocks and the measured time to a straight line
            coef = polyfit(clocks, measuredTime, 1);
            
            % coef(2) is the intercept (a) in the form y = a + bx
            % coef(1) is the slope (b) in the form y = a + bx
            a = coef(2);
            b = coef(1);
            
            measuredAverageSymbolRate = 1/b;
            measuredSymbolRateError = ...
                (measuredAverageSymbolRate - symbolRate)/symbolRate;
            
            reconstructedTime = a + (clocks .* b);
            
            % jitter is the difference between the measured time and the reconstructed time.
            jitter = reconstructedTime - measuredTime;
            
            % see the MATLAB function reference for 'norm'
            rmsJitter = norm(jitter)/sqrt(length(jitter));
            
            set(handles.figJitter3,'HandleVisibility','on');
            axes(handles.axes1)
            plot(waveform)
            title(['symbol rate error: ', num2str(measuredSymbolRateError * 100),'%']);
            xlabel('samples');
            ylabel(['waveform amplitude, ' strtok(yunit,'"')]);
            set(handles.axes1,'XLim',[0 count])
            
            axes(handles.axes2)
            plot(reconstructedTime,jitter);
            title(['RMS jitter: ', num2str(rmsJitter*1e6),' \mus']);
            xlabel('time in seconds');
            ylabel('jitter in \mus');
            % set axis manually, otherwise autoscaling messes up
            set(handles.axes2,'XLim',[0 count*sampleInterval])
            % calculate and plot jitter histogram
            [hs,y]=hist(jitter,30);
            hold on
            % scale the histogram
            hg_hist = barh(y,hs*reconstructedTime(end)/max(hs)*0.3,1);
            hold off
            set(hg_hist,'FaceAlpha',0.4) 
            set(hg_hist,'EdgeAlpha',0) 
            set(handles.figJitter3,'HandleVisibility','off');
            
        else
            set(handles.figJitter3,'HandleVisibility','on');
            axes(handles.axes1)
            plot(waveform)
            xlabel('samples');
            ylabel(['waveform amplitude, ' strtok(yunit,'"')]);
            axes(handles.axes2)
            title('RMS jitter not calculated - no edges detected.');
            set(handles.figJitter3,'HandleVisibility','off');
        end
        % export waveforms to MATLAB workspace
        if exportWaveform
            assignin('base','waveform',waveform);
            assignin('base','measuredTime',measuredTime);
            assignin('base','reconstructedTime',reconstructedTime);
            assignin('base','jitter',jitter);
            assignin('base','clocks',clocks);
            assignin('base','a',a);
            assignin('base','b',b);
            assignin('base','measuredAverageSymbolRate',measuredAverageSymbolRate);
            assignin('base','sampleInterval',sampleInterval);
        end
        drawnow;
        % check whether the user has pressed on Stop button
        interrupted = getappdata(handles.figJitter3,'interrupted');
    else
        set(handles.figJitter3,'HandleVisibility','on');
        axes(handles.axes1)
        title('Data incorrectly received from the scope')
        set(handles.figJitter3,'HandleVisibility','off');
    end    
end % while interrupted
% end of acquire_instrument

